/**
 * 
 */
package com.ws.framework.remoteservice.core;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;

import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.util.ReflectionUtils;

import com.ws.framework.remoteservice.core.annotation.Reference;
import com.ws.framework.remoteservice.core.consumer.PreconditionExecutor;
import com.ws.framework.remoteservice.core.consumer.ServiceAgent;
import com.ws.framework.remoteservice.core.consumer.ServiceLocator;
import com.ws.framework.remoteservice.core.ex.WRSIException;
import com.ws.framework.remoteservice.core.protocal.DockerManager;


/**
 * @author WSH
 *
 */
public class ReferenceBeanPostProcessor implements InstantiationAwareBeanPostProcessor, DisposableBean {

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	@Override
	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
		return null;
	}

	@Override
	public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
		return true;
	}

	@Override
	public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean,
			String beanName) throws BeansException {
		Class<?> beanClass = bean.getClass();
        do {
            for (Field field : beanClass.getDeclaredFields()) {
                Reference reference = field.getAnnotation(Reference.class);
                if (reference != null) {
                    Object service = findService(reference, field.getType());
                    ReflectionUtils.makeAccessible(field);
                    try {
                        field.set(bean, service);
                    } catch (Throwable e) {
                        throw new BeanCreationException(beanName,
                                "Injection of reference dependencies failed", e);
                    }
                }
            }
            beanClass = beanClass.getSuperclass();
        } while (beanClass != null && beanClass != Object.class);
        return pvs;
	}

	 private Object findService(Reference reference, Class<?> serviceType) {
		 	Class<?> contract = reference.contract();
	        String contractName = contract.getName();
	        if  (contract == void.class || contractName.length() == 0) {
	             throw new WRSIException("Contract shouldn't be specified.");
	        }
	        Object service;
	        if (serviceType == ServiceAgent.class) {
	            if (contractName.length() == 0) {
	                throw new WRSIException("Either contract or contractName should be specified.");
	            } else {
	                service = ServiceLocator.getServiceAgent(contract, reference.implCode());
	            }
	        } else {
	            if (contractName.length() != 0) {
	                if (contract != void.class && !serviceType.isAssignableFrom(contract)) {
	                    throw new IllegalStateException("Illegal contract in @Reference annotation.");
	                }
	            } else {
	                contractName = serviceType.getName();
	            }
	            service = ServiceLocator.getService(contract, reference.implCode());
	        }
	        return service;
	    }

	@Override
	public void destroy() throws Exception {
		DockerManager.instance.destroy();
		PreconditionExecutor.instance.destroy();
		
	}

}
